package com.guohao.http;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.guohao.annotation.RequestMapping;
import com.guohao.http.util.RequestParse;
import com.sun.net.httpserver.HttpExchange;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class ServerHandler implements Runnable {

  private final HttpExchange exchange;
  private final Map<String, MethodInfo> methodInfoMap;

  public ServerHandler(HttpExchange exchange, Map<String, MethodInfo> methodInfoMap) {
    this.methodInfoMap = methodInfoMap;
    this.exchange = exchange;
  }

  @Override
  public void run() {
    RequestInfo requestInfo = RequestParse.getRequestInfo(exchange);
    try (OutputStream responseBody = exchange.getResponseBody()) {
      MethodInfo methodInfo = this.methodInfoMap.getOrDefault(
          requestInfo.getUri().concat("@").concat(requestInfo.getRequestMethod().name()), null);
      exchange.getResponseHeaders()
          .add(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue());
      if (methodInfo == null) {
        exchange.sendResponseHeaders(HttpStatus.NOT_FOUND.getValue(), 0);
        responseBody.write(HttpStatus.NOT_FOUND.getReasonPhrase().getBytes());
      } else {
        RequestMethod requestMethod = requestInfo.getRequestMethod();
        Method methodFunc = methodInfo.getMethod();
        RequestMapping annotation = methodFunc.getAnnotation(RequestMapping.class);
        if (annotation.method().compareTo(requestMethod) == 0) {
          Parameter[] parameters = methodFunc.getParameters();
          Object ob;
          if (ObjectUtil.isNotEmpty(parameters)) {
            String body = IoUtil.read(requestInfo.getBody(), Charset.defaultCharset());
            if (ObjectUtil.isNotEmpty(body)) {
              HashMap<String, Object> bodyMap = JSONUtil
                  .toBean(body, new TypeReference<HashMap<String, Object>>() {
                  }, true);
              requestInfo.getQuery().putAll(bodyMap);
            }
            String s = JSONUtil.toJsonStr(requestInfo.getQuery());
            Class<?> type = parameters[0].getType();
            Object o = JSONUtil.toBean(s, type);
            ob = methodFunc
                .invoke(methodInfo.getBean(), o);
          } else {
            ob = methodFunc.invoke(methodInfo.getBean());
          }
          exchange.sendResponseHeaders(200, 0);
          if (ObjectUtil.isNotEmpty(ob)) {
            responseBody.write(JSONUtil.toJsonStr(ob).getBytes(StandardCharsets.UTF_8));
          }
        } else {
          exchange.sendResponseHeaders(HttpStatus.BAD_REQUEST.getValue(), 0);
          responseBody
              .write(HttpStatus.BAD_REQUEST.getReasonPhrase().getBytes(StandardCharsets.UTF_8));
        }
      }
    } catch (Throwable e) {
      try (OutputStream responseBody = exchange.getResponseBody()) {
        exchange.sendResponseHeaders(HttpStatus.INTERNAL_SERVER_ERROR.getValue(), 0);
        responseBody.write(
            HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase().getBytes(StandardCharsets.UTF_8));
      } catch (Exception e1) {
        e1.printStackTrace();
      }
    }
  }
}